/*
 * Copyright (C) 2012  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.soap.axis.client;

import java.net.URL;
import org.apache.axis.client.Stub;
import ca.gc.dfo.wds.BoundaryDate;
import ca.gc.dfo.wds.BoundaryDepth;
import ca.gc.dfo.wds.BoundarySpatial;
import ca.gc.dfo.wds.Metadata;
import ca.gc.dfo.wds.ResultSet;
import ca.gc.dfo.wds.Status;
import ca.gc.shc.ws.client.ObservationsServiceLocator;
import ca.gc.shc.ws.client.ObservationsSoapBindingStub;

import java.net.MalformedURLException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.AxisFault;

/**
 * Class {@code ObservationsClient} eases the communication with the observation
 * web service of
 * <a href="http://www.dfo-mpo.gc.ca/">Fisheries and Oceans Canada</a>.
 * <p>
 * The default URL of the web service is
 * <a href="https://ws-shc.qc.dfo-mpo.gc.ca/observations">https://ws-shc.qc.dfo-mpo.gc.ca/observations</a>.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.soap.axis.client.ObservationsClient">Daniel Léonard</a>
 */
public class ObservationsClient extends WDSClient {

//---------------------------
// enums
//---------------------------

   /**
    * Enum {@code DataType} describes the four possible data type that can be
    * queried from the observations service.
    */
   public enum DataType {
      /** Data type for water level observations. */
      wl,
      /** Data type for salinity observations. */
      sal,
      /** Data type for temperature observations. */
      temp,
      /** Data type for atmospheric pressure observations. */
      atm_pres,
      }


//---------------------------
// Class variables
//---------------------------

   /** The default URL for the Spine service. */
   public static final String DEFAULT_URL = "https://ws-shc.qc.dfo-mpo.gc.ca/observations"; //$NON-NLS-1$


//---------------------------
// Instance variables
//---------------------------

   /** The auto-generated bindings. */
   private ObservationsSoapBindingStub bindings;


//---------------------------
// Constructors
//---------------------------

   /**
    * Constructor.
    * @param   username   the username.
    * @param   password   the password.
    * @exception    AxisFault   if something goes wrong with Axis.
    * @exception    MalformedURLException   if something goes wrong with the
    *                                       default URL.
    * @exception   ServiceException   if something goes wrong with Axis.
    * @see   #DEFAULT_URL
    */
   public ObservationsClient(String username, String password) throws AxisFault, MalformedURLException, ServiceException {
      this(DEFAULT_URL, username, password);
      }

   /**
    * Constructor.
    * @param   url   the web service URL.
    * @param   username   the username.
    * @param   password   the password.
    * @exception    AxisFault   if something goes wrong with Axis.
    * @exception    MalformedURLException   if something goes wrong with the.
    *                                       url.
    * @exception   ServiceException   if something goes wrong with Axis.
    */
   public ObservationsClient(String url, String username, String password) throws AxisFault, MalformedURLException, ServiceException {
      this(new URL(url), username, password);
      }

   /**
    * Constructor.
    * @param   url   the web service URL.
    * @param   username   the username.
    * @param   password   the password.
    * @exception   AxisFault   if something goes wrong with Axis.
    * @exception   ServiceException   if something goes wrong with Axis.
    */
   @SuppressWarnings("hiding")
   public ObservationsClient(URL url, String username, String password) throws AxisFault, ServiceException {
      ObservationsServiceLocator locator = new ObservationsServiceLocator();
      ObservationsSoapBindingStub bindings = (ObservationsSoapBindingStub) locator.getobservations(url);
      bindings.setUsername(username);
      bindings.setPassword(password);
      this.setBindings(bindings);
      }


//---------------------------
// Accessors and mutators
//---------------------------

   /**
    * Changes the Axis bindings.
    * @param   bindings   the new bindings.
    */
   protected void setBindings(ObservationsSoapBindingStub bindings) {
      this.bindings = bindings;
      }

   /**
    * Gets the Axis bindings.
    * @return   the bindings.
    */
   protected ObservationsSoapBindingStub getBindings() {
      return this.bindings;
      }

   /**
    * {@inheritDocs}
    */
   @Override
   protected Stub getBindingsStub() {
      Stub stub = this.getBindings();
      return stub;
      }


//---------------------------
// Delegate methods
//---------------------------

   /**
    * Gets the time interval the data is available for. Boundary dates are
    * expected to be from about two years into the past to a few minutes before
    * the request.
    * @return   the boundary dates.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundaryDate getBoundaryDate() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      BoundaryDate boundaries = bindings.getBoundaryDate();
      return boundaries;
      }

   /**
    * Gets the depth interval the data is available for. The water levels are
    * not concerned with depths as they are taken on the surface. However the
    * salinity and the temperature are taken by sensors below the surface while
    * the atmospheric pressure is taken above the surface (its depth should be
    * seen as an altitude).
    * @return   the boundary depths.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundaryDepth getBoundaryDepth() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      BoundaryDepth boundaries = bindings.getBoundaryDepth();
      return boundaries;
      }

   /**
    * Gets the spatial interval the data is available for. Data is available for
    * ports on the St. Lawrence River between Montréal and the
    * Îles-de-la-Madeleine, roughly between
    * <a href="http://goo.gl/maps/IFo9g">45.4 N 74.0 W</a> and
    * <a href="http://goo.gl/maps/dKgrk">50.2 N 61.8 W</a>.
    * @return   the boundary coordinates.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundarySpatial getBoundarySpatial() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      BoundarySpatial boundaries = bindings.getBoundarySpatial();
      return boundaries;
      }

   /**
    * Gets the list of data types and their description. The observations
    * service has a four data type: {@code wl} for water level, {@code sal} for
    * salinity, {@code temp} for the temperature and {@code atm_pres} for the
    * atmospheric pressure.
    * <p>
    * The water level values are in meters, the salinity is in part per
    * thousand, the temperature is in celcius and the atmospheric pressure is in
    * millibar.
    * <p>
    * One of these values must be given as the first parameter to
    * {@link #search(String, double, double, double, double, double, double, String, String, int, int, boolean, String, SortOrder)}.
    * @return   the list of data types and their descriptions.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getDataInfo() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getDataInfo();
      return info;
      }

   /**
    * Gets the description of the observations service.
    * @return   the description.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getInfo() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      String info = bindings.getInfo();
      return info;
      }

   /**
    * Gets meta data about the observations service.
    * @return   the list of meta data and their value.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getMetadata() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getMetadata();
      return info;
      }

   /**
    * Gets the description of the various meta data.
    * @return   the list of meta data and their description.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getMetadataInfo() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getMetadataInfo();
      return info;
      }

   /**
    * Gets the name of the observations service.
    * @return   the name.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getName() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      String name = bindings.getName();
      return name;
      }

   /**
    * Gets the status of the service.
    * @return   the status.
    * @throws   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Status getStatus() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      Status status = bindings.getStatus();
      return status;
      }

   /**
    * Gets the version of the observations service.
    * @return   the version.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getVersion() throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      String version = bindings.getVersion();
      return version;
      }

   /**
    * Searches water level observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchWaterLevels(double min_lat, double max_lat,
                                      double min_long, double max_long,
                                      long min_date, long max_date,
                                      int start, int size,
                                      boolean metadata, String metadataSelection,
                                      SortOrder order) throws RemoteException {
      String type = DataType.wl.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches water level observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchWaterLevels(double min_lat, double max_lat,
                                      double min_long, double max_long,
                                      String min_date, String max_date,
                                      int start, int size,
                                      boolean metadata, String metadataSelection,
                                      SortOrder order) throws RemoteException {
      String type = DataType.wl.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches salinity observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the minimum depth of the search box.
    * @param   max_depth   the maximum depth of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchSalinity(double min_lat, double max_lat,
                                   double min_long, double max_long,
                                   double min_depth, double max_depth,
                                   long min_date, long max_date,
                                   int start, int size,
                                   boolean metadata, String metadataSelection,
                                   SortOrder order) throws RemoteException {
      String type = DataType.sal.name();
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches salinity observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the minimum depth of the search box.
    * @param   max_depth   the maximum depth of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchSalinity(double min_lat, double max_lat,
                                   double min_long, double max_long,
                                   double min_depth, double max_depth,
                                   String min_date, String max_date,
                                   int start, int size,
                                   boolean metadata, String metadataSelection,
                                   SortOrder order) throws RemoteException {
      String type = DataType.sal.name();
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches temperature observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the minimum depth of the search box.
    * @param   max_depth   the maximum depth of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchTemperature(double min_lat, double max_lat,
                                      double min_long, double max_long,
                                      double min_depth, double max_depth,
                                      long min_date, long max_date,
                                      int start, int size,
                                      boolean metadata, String metadataSelection,
                                      SortOrder order) throws RemoteException {
      String type = DataType.temp.name();
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches temperature observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the minimum depth of the search box.
    * @param   max_depth   the maximum depth of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchTemperature(double min_lat, double max_lat,
                                      double min_long, double max_long,
                                      double min_depth, double max_depth,
                                      String min_date, String max_date,
                                      int start, int size,
                                      boolean metadata, String metadataSelection,
                                      SortOrder order) throws RemoteException {
      String type = DataType.temp.name();
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches atmospheric pressure observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchAtmosphericPressure(
         double min_lat, double max_lat,
         double min_long, double max_long,
         long min_date, long max_date,
         int start, int size,
         boolean metadata, String metadataSelection,
         SortOrder order) throws RemoteException {
      String type = DataType.atm_pres.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches atmospheric pressure observations.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchAtmosphericPressure(
         double min_lat, double max_lat,
         double min_long, double max_long,
         String min_date, String max_date,
         int start, int size,
         boolean metadata, String metadataSelection,
         SortOrder order) throws RemoteException {
      String type = DataType.atm_pres.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(type,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Gets observations.
    * @param   data   the data type, either {@code wl}, {@code sal},
    *                 {@code temp} or {@code atm_pres}.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the minimum depth.
    * @param   max_depth   the maximum depth.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   unused, always {@code ""}.
    * @param   order   the sort order.
    * @return   the list of forecast data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    * @deprecated   Use {@code searchWaterLevels}, {@code searchSalinity},
    *               {@code searchTemperature} or {@code searchAtmosphericPressure}
    *               instead.
    */
   @Override
   @Deprecated
   @SuppressWarnings("hiding")
   public ResultSet search(String data,
                           double min_lat, double max_lat,
                           double min_long, double max_long,
                           double min_depth, double max_depth,
                           String min_date, String max_date,
                           int start, int size,
                           boolean metadata, String metadataSelection,
                           SortOrder order) throws RemoteException {
      ObservationsSoapBindingStub bindings = this.getBindings();
      String sort_order = order.name();
      ResultSet results = bindings.search(data,
                                          min_lat, max_lat,
                                          min_long, max_long,
                                          min_depth, max_depth,
                                          min_date, max_date,
                                          start, size,
                                          metadata, metadataSelection,
                                          sort_order);
      return results;
      }

   }
